package Client;

/*
 * File:    Cliente.java
 * Coms:    Simula un cliente que desea reservar un espacio publicitario para mostrar una imagen.
 * 		    Se conecta de manera síncrona con un banco para crear una cuenta VISA.
 * 		    Se conecta de manera síncrona con un servidor que se encarga de la gestión.
 */

/**
 * @author Daniel Cabrera Ebana - 686013
 * @author Alberto Sanchez Romero - 662385
 * @since 
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import Bank.Banco;

public class Cliente {

	// Direccion y puerto del servidor
	static private String SERVER_ADDRESS;
	static private int SERVER_PORT;
	
	// Direccion y puerto del banco
	static private String SERVER_ADDRESSBANK;
	static private int SERVER_PORTBANK;

	// Creacion del socket con el que se llevarán acabo
	// la comunicación con el servidor.
	static private Socket socketAlServidor = null;

	// variables de trabajo con el servidor y banco
	static private int saldo = 0; // saldo del que dispone el cliente
	static private int tiempo = 0; // tiempo que se muestra la imagen
	static private int valla = 0; // valla en la que se publica una imagen

	/**
	 * @param args
	 * args[0] = Dirección IP Servidor 
	 * args[1] = Puerto Servidor
	 * args[2] = Dirección IP Banco
	 * args[3] = Puerto Banco
	 */
	public static void main(String[] args) {
		boolean conectado;

		// comprobar que existen parametros de entrada
		if (args.length != 4) {
			System.out.println("Por favor introduce: IPServidor PuertoServidor IPBanco PuertoBanco");
			System.exit(0);
		}
		
		// datos de conexion del servidor
		SERVER_ADDRESS = args[0];
		SERVER_PORT = Integer.parseInt(args[1]);

		// datos de conexion del banco
		SERVER_ADDRESSBANK = args[2];
		SERVER_PORTBANK = Integer.parseInt(args[3]);

		// Crear nueva VISA para el cliente
		String VISA = comunicarBanco();

		// En caso de que la visa no sea creada debe abortar
		if (VISA.equals("-2")) {
			System.out
					.println("Error al crear la cuenta, no puede continuar hasta no tener una cuenta");
			System.exit(1);
		}

		// conecta con el servidor y pone a true conectado si se produce la
		// conexion
		conectado = conectarServidor(10);
		if (!conectado) {
			System.err.println("Don't know about host:" + SERVER_ADDRESS);
			System.exit(1); // abortar si hay problemas
		}

		// Una vez realizada la conexion:

		// Inicializacion de los flujos de datos del socket
		// para la comunicacion con el servidor

		PrintWriter canalSalidaAlServidor = null;
		BufferedReader canalEntradaDelServidor = null;
		try {
			canalSalidaAlServidor = new PrintWriter(
					socketAlServidor.getOutputStream(), true);

			canalEntradaDelServidor = new BufferedReader(new InputStreamReader(
					socketAlServidor.getInputStream()));

		} catch (IOException e) { // abortar si hay problemas
			System.err.println("I/O problem:" + SERVER_ADDRESS);
			System.exit(1);
		}
		

		// generar primera petición
		valla = generarValla();
		tiempo = generarTiempo();
		String precioPropuesto = "";
		String userInput = valla + "," + tiempo;
		
		//En caso de que el servidor este terminando su ejecución y no admita mas clientes
		try {
			if(canalEntradaDelServidor.readLine().equals("Apagado")){
				System.out.println("El servidor no acepta más clientes");
				userInput = "refuse";
			}
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		/*
		 * Protocolo de comunicación con el Servidor. Mientras no se reciba
		 * respuesta el cliente seguira esperando. Mostrara por pantalla lo que
		 * le ha devuelto el servidor
		 */
		try {
			int cont = 0;

			// mientras el cliente no rechace la operacion o la realice 3 veces
			while (!(userInput.equals("refuse")) && cont <= 3) {

				if (userInput != null) {
					// envio de oferta
					canalSalidaAlServidor.println(userInput);
					// esperando respuesta del servidor
					String respuesta = canalEntradaDelServidor.readLine();

					if (respuesta.equals("-1")) {
						System.out
								.println("No puedo atenderte, servidor saturado");
						userInput = "refuse";
					} else if (respuesta.equals("0")) {
						System.out
								.println("No puedo atenderte, mensaje erroneo");
					} else if (respuesta.equals("1")) {
						// si la respuesta es buena, puede ser por dos motivos:
						// el cliente acepto la oferta o los datos se pasaron
						// correctamente
						if (userInput.equals("accept")) {// El cliente acepta la
															// oferta
							// mandar visa al servidor
							canalSalidaAlServidor.println(VISA);

							// recibir confirmacion de pago
							// tanto si se realiza o no, se corta la
							// comunicacion
							String confirma = canalEntradaDelServidor
									.readLine();
							
							if (confirma.equals("2")) {// EL pago se ha hecho
								// recibo hora estimada
								System.out.println("Pago realizado");

								System.out.println("Introduce URL de la imagen:");
								
								// COGER URL POR PASO DE TECLADO
								// crea un BufferedReader para la entrada por
								// teclado
								BufferedReader entradaStandard = new BufferedReader(
										new InputStreamReader(System.in));
								String URL = entradaStandard.readLine();

								// Manda al servidor la URL
								canalSalidaAlServidor.println(URL);

								// el servidor comprueba si la url es valida
								// en caso de que la url sea incorrecta vuelve a pedir la imagen
								while (canalEntradaDelServidor.readLine().equals("-1")) {
									System.out.println(canalEntradaDelServidor.readLine());

									System.out.println("Introduce URL de la imagen:");
									// COGER URL POR PASO DE TECLADO
									// crea un BufferedReader para la entrada
									// por teclado
									entradaStandard = new BufferedReader(
											new InputStreamReader(System.in));
									URL = entradaStandard.readLine();

									// Manda al servidor la URL
									canalSalidaAlServidor.println(URL);

								}
								if (canalEntradaDelServidor.readLine().equals("0")) {

									System.out.println("Imagen procesada");

								}
								// resto a mi conocimiento local de saldo el que
								// le ha restado en mi cuenta
								saldo = saldo
										- Integer.parseInt(precioPropuesto);

								// recibir el tiempo que falta para mostrar mi
								// imagen
								String tiempoEstimado="";
								tiempoEstimado = canalEntradaDelServidor.readLine();
								System.out.println("Su imagen se mostrara en la valla " + valla +" en un tiempo estimado de "+ tiempoEstimado);
								
								// en caso de que surja algun error, lo muestra
								// y cierra
							} else if (confirma.equals("-1")) {
								System.out
										.println("Datos de la cuenta incorrectos.");
							} else if (confirma.equals("0")) {
								System.out
										.println("Saldo en la cuenta insuficiente.");
							} else if (confirma.equals("-2")) {
								System.out
										.println("Error al conectar con el banco.");
							} else if (confirma.equals("-3")) {
								System.out
										.println("El pago no se ha realizado.");
							}
							// userInput toma valor refuse para salir del while,
							// haya realizado la operacion con exito o haya
							// habido un erro
							userInput = "refuse";
						} else { // los datos se pasaron correctamente y queda
									// negociar el precio
							// recibo el precio
							precioPropuesto = canalEntradaDelServidor
									.readLine();
							System.out.println("precioPropuesto: "
									+ precioPropuesto);
							// decide negociar el precio y a parte le añade
							// la imagen para mandarlo al servidor
							
							// Crea nueva propuesta de valla y tiempo
							userInput = negociar(
									Integer.parseInt(precioPropuesto), valla,
									tiempo, cont);

							// Si el contador es igual a 3, obliga a abortar por
							// sobrepasar el numero de intentos permitidos
							if (cont == 3) {
								userInput = "refuse";
								// le indica al servidor que se cancela la
								// operacion
								canalSalidaAlServidor.println(userInput);
								System.out
										.println("Numero de intentos superados");
							}
						}

					}
				}
				cont++;
			}

			// Al cerrar cualquiera de los canales de
			// comunicación usados, el socket
			// se cierra. Como no nos importa perder información
			// cerramos el canal de entrada.
			canalEntradaDelServidor.close();

			// Cierre del Socket para comunicarse con el servidor.
			socketAlServidor.close();
		} catch (Exception e) {
			System.err.println(e);
		}

	}

	/**
	 * Comunicación con Banco para generar una nueva VISA y agregar dinero a la
	 * cuenta
	 * 
	 * @return el numero de VISA que se ha generado con la conexion
	 * @see Banco.java
	 */
	static private String comunicarBanco() {

		boolean exito; // conectado?
		String VISA = "";

		exito = conectarBanco(10); // 10 intentos

		if (!exito) {
			System.err.println("Don't know about host:" + SERVER_ADDRESSBANK);
			VISA = "-2"; // Error en la conexion
			System.exit(1); // abortar si hay problemas
		}

		// Ya hay conexion
		// Inicializacion de los flujos de datos del socket
		// para la comunicacion con el servidor

		PrintWriter canalSalidaAlBanco = null;
		BufferedReader canalEntradaDelBanco = null;
		try {
			canalSalidaAlBanco = new PrintWriter(
					socketAlServidor.getOutputStream(), true);
			canalEntradaDelBanco = new BufferedReader(new InputStreamReader(
					socketAlServidor.getInputStream()));
		} catch (IOException e) { // abortar si hay problemas
			System.err.println("I/O problem:" + SERVER_ADDRESSBANK);
			System.exit(1);
		}
		
		//userInput toma el valor "crear cuenta" para que el banco sepa que tipo de operaciones
		//debe ejecutar
		String userInput = "crear cuenta";

		try {
			if (userInput != null) {
				System.out.println("---Creando cuenta bancaria---");
				canalSalidaAlBanco.println(userInput);
				// Generar aleatoriamente un saldo y mandar
				saldo = (int) (Math.random() * (15000 - 20 + 1) + 1);
				System.out.println("Saldo ha introducir en la cuenta: " + saldo);

				canalSalidaAlBanco.println("" + saldo);

				VISA = canalEntradaDelBanco.readLine();
				System.out.println("Su nuevo número de cuenta es: " + VISA);
				if (VISA == null) {
					System.out.println("No se ha podido realizar la operación.");
					System.out.println("Comm. is closed!");
					System.exit(1);
				}
			}
			// Al cerrar cualquiera de los canales de
			// comunicación usados, el socket
			// se cierra. Como no nos importa perder informaci�n
			// cerramos el canal de entrada.
			canalEntradaDelBanco.close();

			// Cierre del Socket para comunicarse con el servidor.
			socketAlServidor.close();
		} catch (Exception e) {
			System.err.println(e);
		}
		return VISA;

	}

	/**
	 * Intenta conectar con el banco un numero de veces marcado por el parametro
	 * de entrada
	 * 
	 * @param maxIntentos
	 * @return true = se ha conectado, false = error
	 */
	static private boolean conectarBanco(int maxIntentos) {
		// hasta maxIntentos intentos de conexi�n, para
		// darle tiempo al servidor a arrancar
		boolean exito = false; // �hay servidor?
		int van = 0;

		while ((van < maxIntentos) && !exito) {
			try {

				socketAlServidor = new Socket(SERVER_ADDRESSBANK,
						SERVER_PORTBANK);
				exito = true;

			} catch (Exception e) {
				van++;
				System.err.println("Failures:" + van);
				try { // esperar 1 seg
					Thread.sleep(1000);
				} catch (InterruptedException e2) {
					e2.printStackTrace();
				}
			}
		}
		return exito;
	}

	/**
	 * Intenta conectar con el servidor un numero de veces marcado por el
	 * parametro de entrada
	 * 
	 * @param maxIntentos
	 * @return true = se ha conectado, false = error
	 */
	static private boolean conectarServidor(int maxIntentos) {
		// hasta maxIntentos intentos de conexi�n, para
		// darle tiempo al servidor a arrancar
		boolean exito = false; // �hay servidor?
		int van = 0;

		while ((van < maxIntentos) && !exito) {
			try {

				socketAlServidor = new Socket(SERVER_ADDRESS, SERVER_PORT);
				exito = true;

			} catch (Exception e) {
				van++;
				System.err.println("Failures:" + van);
				try { // esperar 1 seg
					Thread.sleep(1000);
				} catch (InterruptedException e2) {
					e2.printStackTrace();
				}
			}
		}
		return exito;
	}

	/**
	 * Función que genera un numero de valla aleatorio(1 ó 2)
	 * 
	 * @return numero de valla
	 */
	static private int generarValla() {
		// genera el numero de la valla que quiero(1,2)
		return (int) (Math.random() * (2) + 1);
	}

	/**
	 * Función que genera un tiempo aleatorio entre 5 y 120 segundos
	 * 
	 * @return tiempo en segundos
	 */
	private static int generarTiempo() {
		// tiempo entre 5 y 120 segundos
		return (int) (Math.random() * (120 - 5 + 1) + 5);
	}

	/**
	 * Función que acepta o modifica los terminos de la negociación
	 * 
	 * @param precio a negociar
	 * @param invalla valla a negociar
	 * @param tempo tiempo a negociar
	 * @param intento numero de intentos que ha consumido la negociacion
	 * @return "accept" en caso de aceptar la oferta o un nuevo par de
	 *         parametros(valla,tiempo)
	 */
	private static String negociar(int precio, int invalla, int tempo,
			int intento) {
		// tengo saldo suficiente? precio < 90% saldo->acept
		String respuestaNegocio = "";
		System.out.println("---Realizando negociaciones con el servidor---");
		if (precio < (saldo * 0.99)) {
			respuestaNegocio = "accept";
		} else {
			switch (intento) {
			case 0:
				// si es el primer intento intentara cambiar el tiempo
				tiempo = tempo / 2;
				respuestaNegocio = valla + "," + tiempo;
			case 1:
				// si es el segundo intento intentara cambiar la valla 1 por la
				// 2
				if (invalla == 1) {
					valla = 2;
				}
				respuestaNegocio = valla + "," + tiempo;
			case 2:
				// si es el tercer intento intentara cambiar la valla 1 por la 2
				// y el tiempo
				if (invalla == 1) {
					valla = 2;
				}
				tiempo = tempo / 2;
				respuestaNegocio = valla + "," + tiempo;
			}
		}
		return respuestaNegocio;
	}

}
